package com.rtsapp.server.simulator.socket;

import com.rtsapp.server.common.ByteBuffer;
import com.rtsapp.server.simulator.IDataReceive;
import com.rtsapp.server.simulator.ITcpSocket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;

/**
 * TcpClient
 * 独立的发送，接收线程, 进行数据发送接收
 * 检测到断线后, 会重新连接
 */
public class OriginalTcpSocket implements ITcpSocket {
    private String ip;
    private int port;
    private Socket socket;
    private IDataReceive receive;

    public OriginalTcpSocket(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public void setReceive(  IDataReceive receive ){
        this.receive = receive;
    }



    @Override
    public void connect() {
        try {
            this.socket = new Socket(ip, port);
            // 启动接收线程
            Thread receiveThread = new Thread( new SocketThread(), "SocketThread");
            receiveThread.start();

            System.out.println("\nSuccessfully connected to the " + ip + ":" + port);
        } catch (ConnectException e) {
            System.err.println(e.getMessage());
            throw new RuntimeException( "连接失败", e );
        } catch (IOException e) {
            System.err.println(e.getMessage());
            throw new RuntimeException( "连接失败", e );
        }
    }

    @Override
    public void close() {
        try {
            socket.close();
        } catch (NullPointerException e) {
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void send(byte[] bytes, int length) {
        send(bytes, length, false);
    }

    private void send(byte[] bytes, int length, boolean isRetry) {
        if (socket == null) {
            throw new RuntimeException("Socket Exception...");
        }
        OutputStream os = null;
        try {
            try {
                socket.sendUrgentData(0xFF);
            } catch (Exception ex) {
                System.err.println("Connection is closed, try to reconnect...");
                socket.close();
                connect();
            }
            os = socket.getOutputStream();
            os.write(bytes, 0, length);
            os.flush();
        } catch (IOException e) {
            System.err.println("Connection is closed, try to reconnect...");
            try {
                socket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            connect();
            if (isRetry) {
                System.err.println("Reconnect failed!");
                System.exit(1);
            } else {
                send(bytes, length, true);
            }
        }
    }


    private class SocketThread implements Runnable{

        @Override
        public void run() {
            try {
                while (true) {
                    receive();
                }
            } catch (IOException e) {
                System.err.println("Receiving thread crash! ");
                System.err.println(e.getMessage());
            }
        }

    }


    private void receive() throws IOException {

        int totalReadSize = 0, size;
        byte[] lenBytes = new byte[4];
        while (totalReadSize < lenBytes.length) {
            size = socket.getInputStream().read(lenBytes, totalReadSize, lenBytes.length - totalReadSize);
            totalReadSize += size;
        }
        totalReadSize = 0;
        byte[] bytes = new byte[new ByteBuffer(lenBytes).readInt()];
        while (totalReadSize < bytes.length) {
            size = socket.getInputStream().read(bytes, totalReadSize, bytes.length - totalReadSize);
            totalReadSize += size;
        }
        receive.receiveData(bytes);

    }


    
}
